/*
 * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "atcmd/at_command.h"
#include "at_private.h"
#include "at_debug.h"
#include "os.h"
#include "net/wlan/wlan_defs.h"
#include "net/wlan/wlan.h"
#include "lwip/ip_addr.h"
#include "lwip/dns.h"
#include "net/udhcp/usr_dhcpd.h"
#include "lwip/inet.h"
#include "image/image.h"
#include "driver/chip/hal_wdg.h"
#include "common/framework/net_ctrl.h"

#define PRJCONF_NET_EN                   1
#define PRJCONF_SYSINFO_SAVE_TO_FLASH    1

#include "common/framework/sysinfo.h"

#define CMD_CACHE_MAX_LEN       (1024)
#define CMD_SEND_DATA_MAX_LEN   (1024 + 4)
#define CMD_SEND_TIMEOUT        (10)

char *c_atCmdRspBuf[1024 + 4];

typedef struct cmd_cache {
    u32 cnt;
    u8 buf[CMD_SEND_DATA_MAX_LEN];
} cmd_cache_t;

typedef struct cmd_send_cache {
    u32 status;
    u32 cnt;
    u32 linkID;
    u32 length;
    u8 buf[CMD_SEND_DATA_MAX_LEN];
} cmd_send_cache_t;

typedef struct {
    char *ptr;
} at_para_t;

typedef struct {
    const char *cmd;
    AT_ERROR_CODE (*handler)(at_para_t *at_para);
    const char *usage;
} at_command_handler_t;

typedef struct {
    at_text_t key[AT_PARA_MAX_SIZE];
} at_getcfg_t;

typedef struct {
    at_text_t key[AT_PARA_MAX_SIZE];
} config_key_t;

typedef struct {
    at_text_t ssid[AT_PARA_MAX_SIZE];
} at_ssidtxt_para_t;

typedef struct {
    at_text_t sts_var[AT_PARA_MAX_SIZE];
} at_status_para_t;

typedef struct {
    at_di_t peer_number;
    at_text_t peer_var[AT_PARA_MAX_SIZE];
} at_peers_para_t;

typedef struct {
    at_text_t hostname[AT_PARA_MAX_SIZE];
} at_ping_para_t;

typedef struct {
    at_text_t hostname[AT_PARA_MAX_SIZE];
    at_di_t port;
    at_text_t protocol[1 + 1];
    at_text_t ind[3 + 1];
} at_sockon_para_t;

typedef struct {
    at_text_t id[AT_PARA_MAX_SIZE];
    at_di_t len;
} at_sockw_para_t;

typedef struct {
    at_text_t id[AT_PARA_MAX_SIZE];
} at_sockq_para_t;

typedef struct {
    at_text_t id[AT_PARA_MAX_SIZE];
    at_di_t len;
} at_sockr_para_t;

typedef struct {
    at_text_t id[AT_PARA_MAX_SIZE];
} at_sockc_para_t;

typedef struct {
    at_di_t port;
    at_text_t protocol[1 + 1];
} at_sockd_para_t;

typedef struct {
    at_di_t port;
    at_di_t enable;
    at_di_t max_conn;
} at_tcpserver_para_t;

typedef struct {
    at_di_t value;
} at_wifi_para_t;

typedef struct {
    at_di_t num;
    at_text_t direction[3 + 1];
    at_text_t interrupt[1 + 1];
} at_gpioc_para_t;

typedef struct {
    at_di_t num;
} at_gpior_para_t;

typedef struct {
    at_di_t num;
    at_di_t value;
} at_gpiow_para_t;

typedef struct {
    at_text_t hostname[AT_PARA_MAX_SIZE];
    at_text_t path[1 + 1];
    at_di_t port;
} at_upgrade_para_t;

typedef struct {
    uint8_t ssid[WLAN_SSID_MAX_LEN];
} at_lapscan_para_t;

typedef struct {
    char otaurl[200];
} at_otaurl_para_t;

typedef struct {
    at_text_t mode[1 + 1];
    at_text_t repeat[1 + 1];
} at_scan_para_t;

extern s32 at_get_value(char *strbuf, s32 pt, void *pvar, s32 vsize);
extern s32 at_set_value(s32 pt, void *pvar, s32 vsize, at_value_t *value);
extern int getWifiStationStatus(void);
extern int is_netconnet_ap(void);

extern AT_ERROR_CODE at_get_parameters(char **ppara,
            at_para_descriptor_t *list, s32 lsize, s32 *pcnt);
extern void at_response(AT_ERROR_CODE aec);

extern AT_ERROR_CODE at_act(void);
extern AT_ERROR_CODE at_reset(void);
extern AT_ERROR_CODE at_help(void);
extern AT_ERROR_CODE at_getcfg(char *key);
extern AT_ERROR_CODE at_typecfg(char *key);
extern AT_ERROR_CODE at_setcfg(char *key, at_value_t *value);
extern AT_ERROR_CODE at_ssidtxt(char *ssid);
extern AT_ERROR_CODE at_config(void);
extern AT_ERROR_CODE at_factory(void);
extern AT_ERROR_CODE at_save(void);
extern AT_ERROR_CODE at_status(char *sts_var);
extern AT_ERROR_CODE at_peers(s32 pn, char *pv);
extern AT_ERROR_CODE at_ping(char *hostname);
extern AT_ERROR_CODE at_sockon(char *hostname, s32 port, char *protocol, char *ind);
extern AT_ERROR_CODE at_sockw(char *id, s32 len);
extern AT_ERROR_CODE at_sockq(char *id);
extern AT_ERROR_CODE at_sockr(char *id, s32 len);
extern AT_ERROR_CODE at_sockc(char *id);
extern AT_ERROR_CODE at_sockd(s32 port, char *protocol);
extern AT_ERROR_CODE at_mode(AT_MODE mode);
extern AT_ERROR_CODE at_wifi(s32 value);
extern AT_ERROR_CODE at_reassociate(void);
extern AT_ERROR_CODE at_gpioc(s32 num, char *direction, char *interrupt);
extern AT_ERROR_CODE at_gpior(s32 num);
extern AT_ERROR_CODE at_gpiow(s32 num, s32 value);
extern AT_ERROR_CODE at_upgrade(char *hostname, char *path, s32 port);
extern AT_ERROR_CODE at_scan(char *mode, char *repeat);

static AT_ERROR_CODE attention_handler(at_para_t *at_para);
static AT_ERROR_CODE act_handler(at_para_t *at_para);
static AT_ERROR_CODE reset_handler(at_para_t *at_para);
static AT_ERROR_CODE help_handler(at_para_t *at_para);
static AT_ERROR_CODE getcfg_handler(at_para_t *at_para);
static AT_ERROR_CODE setcfg_handler(at_para_t *at_para);
static AT_ERROR_CODE ssidtxt_handler(at_para_t *at_para);
static AT_ERROR_CODE config_handler(at_para_t *at_para);
static AT_ERROR_CODE factory_handler(at_para_t *at_para);
static AT_ERROR_CODE save_handler(at_para_t *at_para);
static AT_ERROR_CODE status_handler(at_para_t *at_para);
static AT_ERROR_CODE ping_handler(at_para_t *at_para);
static AT_ERROR_CODE sockon_handler(at_para_t *at_para);
static AT_ERROR_CODE sockw_handler(at_para_t *at_para);
static AT_ERROR_CODE sockq_handler(at_para_t *at_para);
static AT_ERROR_CODE sockr_handler(at_para_t *at_para);
static AT_ERROR_CODE sockc_handler(at_para_t *at_para);
static AT_ERROR_CODE sockd_handler(at_para_t *at_para);
static AT_ERROR_CODE mode_handler(at_para_t *at_para);
static AT_ERROR_CODE wifi_handler(at_para_t *at_para);
static AT_ERROR_CODE reassociate_handler(at_para_t *at_para);
static AT_ERROR_CODE scan_handler(at_para_t *at_para);

typedef struct {
    at_di_t uart_id;
    at_di_t baudrate;
    at_di_t data_bits;
    at_di_t stop_bits;
    at_di_t parity;
    at_di_t hwfc;
} at_uart_para_t;

typedef struct {
    int sleepMode;
} at_sleep_para_t;

typedef struct {
    int powersaveMode;
} at_powersave_para_t;

// typedef struct {
//     int echoSwitch;
// } at_echoswitch_para_t;

typedef struct {
    int autoconnectSwitch;
} at_autoconnect_para_t;

typedef struct {
    int IPDinfoSwitch;
} at_ipdinfo_para_t;

typedef struct {
    at_di_t gpioId;
    at_di_t edge;
} at_gpiowakeup_para_t;

typedef struct {
    at_di_t wifi_mode;
} at_wifiMode_para_t;

typedef struct {
    at_text_t ssid[AT_PARA_MAX_SIZE];
    at_text_t pwd[AT_PARA_MAX_SIZE];
} at_joinAp_para_t;

#define CMD_WLAN_MAX_BSS_CNT    50

enum atc_scanap_ecntype {
    ATC_SCANAP_ECN_OPEN         = 0,
    ATC_SCANAP_ECN_WEP          = 1,
    ATC_SCANAP_ECN_WPA_PSK      = 2,
    ATC_SCANAP_ECN_WPA_WPA2_PSK = 3,
};

typedef struct {
    at_text_t ssid[AT_PARA_MAX_SIZE];
} at_getscan_para_t;

typedef struct {
    at_di_t dhcpmode;
    at_di_t dhcpen;
} at_dhcp_para_t;

typedef struct {
    at_di_t en;
    at_di_t lease_time;
    at_ip_t start_ip;
    at_ip_t end_ip;
} at_dhcp_range_para_t;

typedef struct {
    at_hex_t mac[6];
} at_mac_para_t;

typedef struct {
    at_ip_t ip;
    at_ip_t getway;
    at_ip_t netmask;
} at_ip_para_t;

typedef struct {
    at_text_t hostname[AT_PARA_MAX_SIZE];
} at_hostname_para_t;

static AT_ERROR_CODE version_handler(at_para_t *at_para);
static AT_ERROR_CODE echo_on_handler(at_para_t *at_para);
static AT_ERROR_CODE echo_off_handler(at_para_t *at_para);
static AT_ERROR_CODE etf_enter_handler(at_para_t *at_para);
static AT_ERROR_CODE restore_handler(at_para_t *at_para);
static AT_ERROR_CODE uart_config_handler(at_para_t *at_para);
static AT_ERROR_CODE uart_config_save_handler(at_para_t *at_para);
static AT_ERROR_CODE sleep_handler(at_para_t *at_para);
static AT_ERROR_CODE powersave_handler(at_para_t *at_para);
static AT_ERROR_CODE wakeupgpio_handler(at_para_t *at_para);
static AT_ERROR_CODE wifi_mode_handler(at_para_t *at_para);
static AT_ERROR_CODE join_ap_handler(at_para_t *at_para);
static AT_ERROR_CODE scan_attr_handler(at_para_t *at_para);
static AT_ERROR_CODE disconnect_handler(at_para_t *at_para);
static AT_ERROR_CODE setautoconnect_handler(at_para_t *at_para);
static AT_ERROR_CODE set_dhcp_handler(at_para_t *at_para);
static AT_ERROR_CODE set_mac_handler(at_para_t *at_para);
static AT_ERROR_CODE set_ip_handler(at_para_t *at_para);
static AT_ERROR_CODE set_ap_mode_handler(at_para_t *at_para);
static AT_ERROR_CODE set_hostname_handler(at_para_t *at_para);
static AT_ERROR_CODE dhcpd_start_handler(at_para_t *at_para);
static AT_ERROR_CODE network_status_handler(at_para_t *at_para);
static AT_ERROR_CODE dns_handler(at_para_t *at_para);
static AT_ERROR_CODE ap_server_handler(at_para_t *at_para);
static AT_ERROR_CODE ap_server_conn_handler(at_para_t *at_para);
static AT_ERROR_CODE disp_apinfo_handler(at_para_t *at_para);
static AT_ERROR_CODE disp_ipinfo_handler(at_para_t *at_para);
static AT_ERROR_CODE create_tcp_udp_handler(at_para_t *at_para);
static AT_ERROR_CODE set_apcfg_handler(at_para_t *at_para);
static AT_ERROR_CODE send_tcp_handler(at_para_t *at_para);
static AT_ERROR_CODE close_network_handler(at_para_t *at_para);
static AT_ERROR_CODE set_multi_connect_handler(at_para_t *at_para);
static AT_ERROR_CODE set_transport_mode_handler(at_para_t *at_para);
static AT_ERROR_CODE set_dns_handler(at_para_t *at_para);
static AT_ERROR_CODE read_tcp_data_handler(at_para_t *at_para);
static AT_ERROR_CODE ipdinfo_handler(at_para_t *at_para);
static AT_ERROR_CODE set_recv_data_mode_handler(at_para_t *at_para);
static AT_ERROR_CODE set_hostname_handler(at_para_t *at_para);
static AT_ERROR_CODE send_data_handler(at_para_t *at_para);

extern AT_ERROR_CODE at_version(char *version);
extern AT_ERROR_CODE at_restore(char *address);
extern AT_ERROR_CODE at_uart_config(int uartId, int uartBaud, int dataBit, int parity, int stopBit, int hwfc);
extern AT_ERROR_CODE at_sleep(int sleepMode);
extern AT_ERROR_CODE at_wakeupgpio(int gpioId);
extern AT_ERROR_CODE at_set_tcpserver(int port, int enable);
extern AT_ERROR_CODE at_tcp_max_conn(int max_conn);
extern AT_ERROR_CODE at_wifi_mode(int wifiMode);
extern AT_ERROR_CODE at_setwupio(int wifiMode, int edge);
extern AT_ERROR_CODE at_join_ap(char *ssid, char *pwd);
extern AT_ERROR_CODE at_get_apinfo(void);
extern AT_ERROR_CODE at_scan_attr(char *at_para);
extern AT_ERROR_CODE at_disconnect(char *at_para);
extern AT_ERROR_CODE at_set_dhcp(unsigned char *at_para);
extern AT_ERROR_CODE at_set_mac(unsigned char *at_para);
extern AT_ERROR_CODE at_set_ip(unsigned char *at_para);
extern AT_ERROR_CODE at_set_hostname(char *at_para);
static AT_ERROR_CODE CWLAPscan_handler(at_para_t *at_para);
static AT_ERROR_CODE set_gpio_cfg_handler(at_para_t *at_para);
static AT_ERROR_CODE get_gpio_cfg_handler(at_para_t *at_para);
static AT_ERROR_CODE set_gpio_dir_handler(at_para_t *at_para);
static AT_ERROR_CODE set_gpio_handler(at_para_t *at_para);
static AT_ERROR_CODE read_gpio_handler(at_para_t *at_para);
static AT_ERROR_CODE CWLAPOPTscan_handler(at_para_t *at_para);
static AT_ERROR_CODE start_smartconfig_handler(at_para_t *at_para);
static AT_ERROR_CODE stop_smartconfig_handler(at_para_t *at_para);
static AT_ERROR_CODE start_DISCOVER_handler(at_para_t *at_para);
static AT_ERROR_CODE stop_DISCOVER_handler(at_para_t *at_para);
static AT_ERROR_CODE sendbuf_status_handler(at_para_t *at_para);
static AT_ERROR_CODE set_translink_save_handler(at_para_t *at_para);
static AT_ERROR_CODE ap_server_timeout_handler(at_para_t *at_para);
static AT_ERROR_CODE ap_server_receive_handler(at_para_t *at_para);
static AT_ERROR_CODE ap_server_receivedata_handler(at_para_t *at_para);
static AT_ERROR_CODE ap_server_receivelen_handler(at_para_t *at_para);
static AT_ERROR_CODE ota_handler(at_para_t *at_para);

at_callback_t at_callback;
static cmd_cache_t cache;
static cmd_send_cache_t send_cache;
static int reconnect_enable = 1;

static const at_command_handler_t at_command_table[] = {
    {"AT",                  attention_handler,        " -- Null cmd, always returns OK"},
    {"AT+ACT",              act_handler,              " -- Switch WiFi work mode"},
    {"AT+RST",              reset_handler,            " -- Reset the module"},
    {"AT+S.HELP",           help_handler,             " -- This text"},
    {"AT+S.GCFG",           getcfg_handler,           " =<key> -- Get config key"},
    {"AT+S.SCFG",           setcfg_handler,           " =<key>,<value> -- Set config key"},
    {"AT+S.SSIDTXT",        ssidtxt_handler,          " [=<ssidtxt>] -- Set a textual SSID (not hex), otherwise prints current SSID",},
    {"AT&V",                config_handler,           " -- Dump all settings",    },
    {"AT&F",                factory_handler,          " -- Restore factory default settings"},
    {"AT&W",                save_handler,             " -- Save current settings to flash",},
    {"AT+S.STS",            status_handler,           " [=<sts_var>] -- Report current status/statistics",},
    {"AT+S.PING",           ping_handler,             " =<hostname> -- Send a ping to a specified host"},
    {"AT+S.SOCKON",         sockon_handler,           " =<hostname>,<port>,<t|u> -- Open a network socket"},
    {"AT+S.SOCKW",          sockw_handler,            " =<id>,<len> -- Write data to socket"},
    {"AT+S.SOCKQ",          sockq_handler,            " =<id> -- Query socket for pending data",},
    {"AT+S.SOCKR",          sockr_handler,            " =<id>,<len> -- Read data from socket"},
    {"AT+S.SOCKC",          sockc_handler,            " =<id> -- Close socket"},
    {"AT+S.SOCKD",          sockd_handler,            " =<0|port>,<t|u> -- Disable/Enable socket server. Default is TCP"},
    {"AT+S.",               mode_handler,             " -- Switch to data mode",},
    {"AT+S.WIFI",           wifi_handler,             " =<0|1> -- Disable/Enable WiFi"},
    {"AT+S.ROAM",           reassociate_handler,      " -- Trigger a WiFi Roam"},
    {"AT+S.SCAN",           scan_handler,             " -- Perform a scan"},

    {"ATE1",                echo_on_handler,          " -- echo on/off"},
    {"ATE0",                echo_off_handler,         " -- echo on/off"},
    {"etf",                 etf_enter_handler,        " -- enter etf mode"},
    {"AT+GMR",              version_handler,          " -- version info"},
    {"AT+UART_CUR",         uart_config_handler,      " -- config uart don't save into flash"},
    {"AT+UART_DEF",         uart_config_save_handler, " -- config uart save into flash"},
    {"AT+RESTORE",          restore_handler,          " -- erase all infomation"},
    {"AT+CWJAP",            join_ap_handler,          " -- join ap and do not save into flash"},
    {"AT+CWJAP_CUR",        join_ap_handler,          " -- join ap and do not save into flash"},
    {"AT+CWJAP_DEF",        join_ap_handler,          " -- join ap and save into flash"},
    {"AT+CWLAPOPT",         CWLAPOPTscan_handler,     " -- scan the ap near"},
    {"AT+CWLAP",            CWLAPscan_handler,        " -- scan the ap near"},
    {"AT+CWQAP",            disconnect_handler,       " -- disconnect ap "},
    {"AT+CWAUTOCONN",       setautoconnect_handler,   " -- setautoconnect funciton "},
    {"AT+CWDHCP",           set_dhcp_handler,         " -- set dhcp address"},
    {"AT+CWDHCP_CUR",       set_dhcp_handler,         " -- set dhcp address"},
    {"AT+CWDHCP_DEF",       set_dhcp_handler,         " -- set dhcp address"},
    {"AT+CIPSTAMAC_DEF",    set_mac_handler,          " -- set mac address"},
    {"AT+CIPSTAMAC_CUR",    set_mac_handler,          " -- set mac address"},
    {"AT+CIPSTAMAC",        set_mac_handler,          " -- set mac address"},
    {"AT+PING",             ping_handler,             " -- ping work"},
    {"AT+CIPSTA_CUR",       set_ip_handler,           " -- set ip address "},
    {"AT+CIPSTA",           set_ip_handler,           " -- set ip address "},
    {"AT+CIPSTA_DEF",       set_ip_handler,           " -- set ip address "},
    {"AT+CIPAP_DEF",        set_ap_mode_handler,      " -- set to ap mode "},
    {"AT+CWDHCPS_DEF",      dhcpd_start_handler,      " -- set dhcp ranges "},

    {"AT+CIPSTART",         create_tcp_udp_handler,        " -- create udp or tcp connection"},
    {"AT+CIPCLOSE",         close_network_handler,         " -- close the network"},
    {"AT+CIPSEND",          send_data_handler,             " -- send the data"},
    {"AT+CIPSTATUS",        network_status_handler,        " -- query network status"},
    {"AT+CIPSERVER",        ap_server_handler,             " -- set tcp server"},
    {"AT+CIPSTO",            ap_server_timeout_handler,    " -- set tcp server"},
    {"AT+CIPSERVERMAXCONN", ap_server_conn_handler,        " -- set tcp server max connects"},
    {"AT+CIPRECVMODE",      ap_server_receive_handler,     " -- set tcp server max connects"},
    {"AT+CIPRECVDATA",      ap_server_receivedata_handler, " -- set tcp server"},
    {"AT+CIPRECVLEN",       ap_server_receivelen_handler,  " -- set tcp server"},

    {"AT+CWLIF",            disp_apinfo_handler,        " -- set tcp server max connects"},
    {"AT+CIFSR",            disp_ipinfo_handler,        " -- display all ips "},
    {"AT+CWSAP_DEF",        set_apcfg_handler,          " -- display all ips "},
    {"AT+PS",               powersave_handler,          " -- enter into powersave mode"},
    {"AT+SLEEP",            sleep_handler,              " -- enter into sleep mode"},
    {"AT+WAKEUPGPIO",       wakeupgpio_handler,         " -- wake up by gpio"},
    {"AT+CWMODE",           wifi_mode_handler,          " -- set the wifi mode save into flash"},
    {"AT+CWMODE_DEF",       wifi_mode_handler,          " -- set the wifi mode save into flash"},
    {"AT+CWLAPOPT",         scan_attr_handler,          " -- set scan result attr"},
    {"AT+CWHOSTNAME",       set_hostname_handler,       " -- set network hostname"},
    {"AT+CWSTARTSMART",     start_smartconfig_handler,  " -- set network hostname"},
    {"AT+CWSTOPSMART",      stop_smartconfig_handler,   " -- set network hostname"},
    {"AT+CWSTARTDISCOVER",  start_DISCOVER_handler,     " -- set network hostname"},
    {"AT+CWSTOPDISCOVER",   stop_DISCOVER_handler,      " -- set network hostname"},

    {"AT+CIPDOMAIN",        dns_handler,                " -- dns parse"},
    {"AT+CIPSENDBUF",       send_tcp_handler,           " -- write data into tcp buffer "},
    {"AT+CIPBUFSTATUS",     sendbuf_status_handler,     " -- write data into tcp buffer "},
    {"AT+CIPMUX",           set_multi_connect_handler,  " -- set mutil connection mode"},
    {"AT+SAVETRANSLINK",    set_translink_save_handler, " -- set mutil connection mode"},
    {"AT+CIPMODE",          set_transport_mode_handler, " -- set trasport mode "},
    {"AT+CIPDNS",           set_dns_handler,            " -- set dns"},
    {"AT+CIPRECVDATA",      read_tcp_data_handler,      " -- read the data from tcp buffer"},
    {"AT+CIPDINFO",         ipdinfo_handler,            " --set whether disp the IPD char"},
    {"AT++CIPRECVMODE",     set_recv_data_mode_handler, " -- set the tcp recv data mode"},
    {"AT+SYSIOSETCFG",      set_gpio_cfg_handler,       "-- set the gpio work mode"},
    {"AT+SYSIOGETCFG",      get_gpio_cfg_handler,       "-- get the gpio work mode"},
    {"AT+SYSGPIODIR",       set_gpio_dir_handler,       "-- set the gpio dir mode"},
    {"AT+SYSGPIOWRITE",     set_gpio_handler,           "-- set the gpio"},
    {"AT+SYSGPIOREAD",      read_gpio_handler,          "-- read the gpio"},
    {"AT+OTA",              ota_handler,                "-- ota function"},
};

AT_ERROR_CODE at_help(void)
{
    s32 i;

    for (i = 0; i < TABLE_SIZE(at_command_table); i++) {
        at_dump("# %s%s\r\n", at_command_table[i].cmd,
            at_command_table[i].usage);
    }

    return AEC_OK;
}

static s32 at_match(char *cmd)
{
    s32 i;

    if (cmd == NULL) {
        return -2;
    }

    for (i = 0; i < TABLE_SIZE(at_command_table); i++) {
        if (!strcmp(cmd, at_command_table[i].cmd)) {
            return i;
        }
    }

    return -1;
}

/**
  * @brief  AT command initializer.
  * @param    cb: AT command callback function
  * @retval AEC_OK: succeed        Other: fail
  */
AT_ERROR_CODE at_init(at_callback_t *cb)
{
    //at_callback_para_t para;

    memset(&at_callback, 0, sizeof(at_callback));

    if (cb == NULL) {
        return AEC_UNDEFINED;
    }

    at_callback = *cb;
#if 0
    para.cfg = &at_cfg;

    if (at_callback.handle_cb != NULL) {
        if (at_callback.handle_cb(ACC_LOAD, &para, NULL) != AEC_OK) {
            at_callback.handle_cb(ACC_FACTORY, &para, NULL);
        }
    }
#endif
    memset(&cache, 0, sizeof(cache));

    return AEC_OK;
}

static AT_ERROR_CODE at_parse_cmd(char *cmdline, s32 size)
{
    char at_cmd[AT_CMD_MAX_SIZE + 1];
    at_para_t at_para;
    char *cptr;
    s32 idx;
    s32 cnt = 0;
    s32 i;

    cptr = cmdline;

    for (i = 0; i < size; i++) {
        if (*cptr == AT_EQU) {
            break;
        } else if (*cptr == AT_LF) {
            break;
        } else if (*cptr == AT_QUE) {
            break;
        } else if (*cptr == AT_CR) {
            if (((i + 1) < size) && (*(cptr + 1) == AT_LF)) {
                break;
            } else {
                break;
            }
        } else {
            if (cnt < sizeof(at_cmd) - 1) {
                at_cmd[cnt++] = *cptr++;
            } else {
                return AEC_CMD_ERROR;
            }
        }
    }

    at_cmd[cnt] = '\0';    /* add string termination */

    if (cnt == 0) {        /* skip blank line */
        return AEC_BLANK_LINE;
    }

    idx = at_match(at_cmd);

    if (idx >= 0) {
        if (at_command_table[idx].handler != NULL) {
            at_para.ptr = cptr;
            return at_command_table[idx].handler(&at_para);
        } else {
            return AEC_CMD_ERROR;
        }
    } else {
        return AEC_CMD_ERROR;
    }
}

/**
  * @brief  AT command parser.
  * @param    none
  * @retval AEC_OK: succeed        Other: fail
  */
AT_ERROR_CODE at_parse(void)
{
    AT_ERROR_CODE aec = AEC_OK;
    AT_QUEUE_ERROR_CODE aqec = AQEC_EMPTY;
    u8 tmp;
    u8 send_timeout_flag = 0;
    u32 flag = 0;
    u32 t0 = 0;

    memset(&send_cache, 0, sizeof(cmd_send_cache_t));
    memset(&cache, 0, sizeof(cmd_cache_t));
    while (1) {

        aqec = at_queue_get(&tmp);
        if (aqec == AQEC_OK) {
            if (send_cache.status == 0) {
                if (tmp == AT_LF) {
                    if (cache.cnt < CMD_CACHE_MAX_LEN) {
                        cache.buf[cache.cnt++] = tmp;
                    } else {
                        cache.cnt = 0;
                        AT_DBG("command is discarded!\n");
                        continue;    /* command is discarded */
                    }

                    flag = 1;
                } else if (tmp == AT_CR) {
                    cache.buf[cache.cnt++] = tmp;

                    aqec = at_queue_peek(&tmp);
                    if (aqec == AQEC_OK && tmp == AT_LF) {
                        aqec = at_queue_get(&tmp);
                        if (cache.cnt < CMD_CACHE_MAX_LEN) {
                            cache.buf[cache.cnt++] = tmp;
                        } else {
                            cache.cnt = 0;
                            AT_DBG("command is discarded!\n");
                            continue;    /* command is discarded */
                        }
                    }
                    flag = 1;
                }
            } else if (send_cache.status == 1) {
                send_timeout_flag = 1;
                t0 = OS_GetTicks();
                send_cache.buf[send_cache.cnt++] = tmp;
                if (send_cache.cnt >= send_cache.length) {
                    if (send_cache.cnt >= CMD_CACHE_MAX_LEN) {
                        send_cache.cnt = 0;
                    }
                    memset(&cache, 0, sizeof(cmd_cache_t));
                    memcpy(cache.buf, "AT+CIPSEND=0,1,u",
                           sizeof("AT+CIPSEND=0,1,u"));
                    cache.cnt = sizeof("AT+CIPSEND=0,1,u");
                    flag = 1;
                    send_timeout_flag = 0;
                    continue;
                }
            }

            if (cache.cnt < CMD_CACHE_MAX_LEN) {
                cache.buf[cache.cnt++] = tmp;
            } else {
                cache.cnt = 0;
                AT_DBG("command is discarded!\n");
                continue;    /* command is discarded */
            }
        }

        if (send_timeout_flag == 1) {
            if (OS_GetTicks() - t0 > CMD_SEND_TIMEOUT * 1000) {
                send_timeout_flag = 0;
                memset(&send_cache, 0,
                       sizeof(cmd_send_cache_t));
                memset(&cache, 0, sizeof(cmd_cache_t));
                at_response(AEC_SEND_TIMEOUT);
            }
        }
        if (flag) {
            /* echo */
            if (at_cfg.localecho1) {
                cache.buf[cache.cnt] = '\0';
                at_dump("%s", cache.buf);
            }

            aec = at_parse_cmd((char *)cache.buf, cache.cnt);

            if (send_cache.status == 1 && aec != AEC_SEND_READY) {
                memset(&send_cache, 0,
                       sizeof(cmd_send_cache_t));
            }
            at_response(aec);

            cache.cnt = 0;

            flag = 0;
        }

    }

    return AEC_OK;
}

static AT_ERROR_CODE attention_handler(at_para_t *at_para)
{
    int res;

    res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

    return res;
}

static AT_ERROR_CODE act_handler(at_para_t *at_para)
{
    AT_ERROR_CODE res;

    res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

    if (res != AEC_OK) {
        return AEC_PARA_ERROR;
    } else {
        return at_act();
    }
}

static AT_ERROR_CODE reset_handler(at_para_t *at_para)
{
    AT_ERROR_CODE res;

    res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

    if (res != AEC_OK) {
        return AEC_PARA_ERROR;
    } else {
        return at_reset();
    }
}

static AT_ERROR_CODE help_handler(at_para_t *at_para)
{
    AT_ERROR_CODE res;

    res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

    if (res != AEC_OK) {
        return AEC_PARA_ERROR;
    } else {
        return at_help();
    }
}

static AT_ERROR_CODE getcfg_handler(at_para_t *at_para)
{
    at_getcfg_t cmd_para = {    /* default value */
        ""
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT,    &cmd_para.key,  AET_LINE | SIZE_LIMIT(sizeof(cmd_para.key))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt != 1) {
            return AEC_PARA_ERROR;
        }

        return at_getcfg(cmd_para.key);
    }
}

static AT_ERROR_CODE setcfg_handler(at_para_t *at_para)
{
    config_key_t config_key;
    at_value_t config_value;
    at_para_descriptor_t cmd_key_list[] = {
        {APT_TEXT, &config_key.key, AET_PARA | SIZE_LIMIT(sizeof(config_key.key))},
    };
    at_para_descriptor_t cmd_value_list[] = {
        {APT_TEXT, &config_value.text, AET_LINE | SIZE_LIMIT(sizeof(config_value.text))},
        {APT_HEX,  &config_value.hex,  AET_LINE | SIZE_LIMIT(sizeof(config_value.hex))},
        {APT_DI,   &config_value.di,   AET_LINE | SIZE_LIMIT(sizeof(config_value.di))},
        {APT_HI,   &config_value.hi,   AET_LINE | SIZE_LIMIT(sizeof(config_value.di))},
        {APT_IP,   &config_value.ip,   AET_LINE | SIZE_LIMIT(sizeof(config_value.ip))},
    };
    s32 paracnt;
    s32 res;
    s32 type;
    s32 idx;
    s32 match = 0;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */
        memset(&config_key, 0, sizeof(config_key));    /* default value */
        res = at_get_parameters(&at_para->ptr, cmd_key_list,
                      TABLE_SIZE(cmd_key_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt != 1) {
            return AEC_PARA_ERROR;
        }

        type = at_typecfg(config_key.key);

        for (idx = 0; idx < TABLE_SIZE(cmd_value_list); idx++) {
            if (cmd_value_list[idx].pt == type) {
                match = 1;
                break;
            }
        }

        if (!match) {
            return AEC_NOT_FOUND;
        } else {
            memset(&config_value, 0, sizeof(config_value));    /* default value */
            res = at_get_parameters(&at_para->ptr,
                          &cmd_value_list[idx], 1,
                          &paracnt);

            if (res != AEC_OK) {
                return AEC_PARA_ERROR;
            }

            if (paracnt != 1) {
                return AEC_PARA_ERROR;
            }

            return at_setcfg(config_key.key, &config_value);
        }
    }
}

static AT_ERROR_CODE ssidtxt_handler(at_para_t *at_para)
{
    at_ssidtxt_para_t cmd_para = {    /* default value */
        ""
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &cmd_para.ssid, AET_LINE | SIZE_LIMIT(sizeof(cmd_para.ssid))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;;
        }

        if (paracnt != 1) {
            return AEC_PARA_ERROR;
        }

        return at_ssidtxt(cmd_para.ssid);
    }
}

static AT_ERROR_CODE config_handler(at_para_t *at_para)
{
    int res;

    res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

    if (res != AEC_OK) {
        return AEC_PARA_ERROR;
    } else {
        return at_config();
    }
}

static AT_ERROR_CODE factory_handler(at_para_t *at_para)
{
    int res;

    res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

    if (res != AEC_OK) {
        return AEC_PARA_ERROR;
    } else {
        return at_factory();
    }
}

static AT_ERROR_CODE save_handler(at_para_t *at_para)
{
    int res;

    res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

    if (res != AEC_OK) {
        return AEC_PARA_ERROR;
    }

    return at_save();
}

static AT_ERROR_CODE status_handler(at_para_t *at_para)
{
    at_status_para_t cmd_para = {    /* default value */
        ""
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &cmd_para.sts_var, AET_LINE | SIZE_LIMIT(sizeof(cmd_para.sts_var))},
    };
    s32 paracnt;
    int res;

    if (*at_para->ptr != AT_EQU) {
        res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        } else {
            return at_status(NULL);
        }
    } else {
        at_para->ptr++;    /* skip '=' */

        res = at_get_parameters(&at_para->ptr, cmd_para_list, TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt != 1) {
            return AEC_PARA_ERROR;
        }

        return at_status(cmd_para.sts_var);
    }
}

static AT_ERROR_CODE ping_handler(at_para_t *at_para)
{
    at_ping_para_t cmd_para = {    /* default value */
        ""
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &cmd_para.hostname, AET_LINE | SIZE_LIMIT(sizeof(cmd_para.hostname))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt != 1) {
            return AEC_PARA_ERROR;
        }

        AT_WRN("%s\n", cmd_para.hostname);
        return at_ping(cmd_para.hostname);
    }
}

static AT_ERROR_CODE sockon_handler(at_para_t *at_para)
{
    at_sockon_para_t cmd_para = {    /* default value */
        "",
        0,
        "t",
        ""
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &cmd_para.hostname, AET_PARA | SIZE_LIMIT(sizeof(cmd_para.hostname))},
        {APT_DI,   &cmd_para.port,     AET_PARA | SIZE_LIMIT(sizeof(cmd_para.port))},
        {APT_TEXT, &cmd_para.protocol, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(cmd_para.protocol))},
        {APT_TEXT, &cmd_para.ind,      AET_LINE | SIZE_LIMIT(sizeof(cmd_para.ind))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 3) {
            return AEC_PARA_ERROR;
        }

        if (cmd_para.port < 0 || cmd_para.port > 65535) {
            return AEC_OUT_OF_RANGE;
        }

        if (!(!strcmp(cmd_para.protocol, "t") || !strcmp(cmd_para.protocol, "u"))) {
            return AEC_PARA_ERROR;
        }

        if (paracnt == 4) {
            if (!(!strcmp(cmd_para.ind, "ind"))) {
                return AEC_PARA_ERROR;
            }
        }

        return at_sockon(cmd_para.hostname, cmd_para.port,
                 cmd_para.protocol, cmd_para.ind);
    }
}

static AT_ERROR_CODE sockw_handler(at_para_t *at_para)
{
    at_sockw_para_t cmd_para = {    /* default value */
        "",
        0
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &cmd_para.id,  AET_PARA | SIZE_LIMIT(sizeof(cmd_para.id))},
        {APT_DI,   &cmd_para.len, AET_LINE | SIZE_LIMIT(sizeof(cmd_para.len))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 2) {
            return AEC_PARA_ERROR;
        }

        return at_sockw(cmd_para.id, cmd_para.len);
    }
}

static AT_ERROR_CODE sockq_handler(at_para_t *at_para)
{
    at_sockq_para_t cmd_para = {    /* default value */
        ""
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &cmd_para.id, AET_LINE | SIZE_LIMIT(sizeof(cmd_para.id))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }

        return at_sockq(cmd_para.id);
    }
}

static AT_ERROR_CODE sockr_handler(at_para_t *at_para)
{
    at_sockr_para_t cmd_para = {    /* default value */
        "",
        0
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &cmd_para.id,  AET_PARA | SIZE_LIMIT(sizeof(cmd_para.id))},
        {APT_DI,   &cmd_para.len, AET_LINE | SIZE_LIMIT(sizeof(cmd_para.len))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 2) {
            return AEC_PARA_ERROR;
        }

        return at_sockr(cmd_para.id, cmd_para.len);
    }
}

static AT_ERROR_CODE sockc_handler(at_para_t *at_para)
{
    at_sockc_para_t cmd_para = {    /* default value */
        ""
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &cmd_para.id, AET_LINE | SIZE_LIMIT(sizeof(cmd_para.id))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }

        return at_sockc(cmd_para.id);
    }
}

static AT_ERROR_CODE sockd_handler(at_para_t *at_para)
{
    at_sockd_para_t cmd_para = {    /* default value */
        0,
        "t"
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI,   &cmd_para.port,     AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(cmd_para.port))},
        {APT_TEXT, &cmd_para.protocol, AET_LINE | SIZE_LIMIT(sizeof(cmd_para.protocol))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }

        if (cmd_para.port < 0 || cmd_para.port > 65535) {
            return AEC_OUT_OF_RANGE;
        }

        if (!(!strcmp(cmd_para.protocol, "t") || !strcmp(cmd_para.protocol, "u"))) {
            return AEC_PARA_ERROR;
        }

        return at_sockd(cmd_para.port, cmd_para.protocol);
    }
}

static AT_ERROR_CODE mode_handler(at_para_t *at_para)
{
    int res;

    res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

    if (res != AEC_OK) {
        return AEC_PARA_ERROR;
    } else {
        return at_mode(AM_DATA);
    }
}

static AT_ERROR_CODE wifi_handler(at_para_t *at_para)
{
    at_wifi_para_t cmd_para = {    /* default value */
        0
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &cmd_para.value, AET_LINE | SIZE_LIMIT(sizeof(cmd_para.value))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        at_para->ptr++;    /* skip '=' */
        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt != 1) {
            return AEC_PARA_ERROR;
        }

        if (cmd_para.value >= 0 && cmd_para.value <= 1) {
            return at_wifi(cmd_para.value);
        } else {
            return AEC_OUT_OF_RANGE;
        }
    }
}

static AT_ERROR_CODE reassociate_handler(at_para_t *at_para)
{
    int res;

    res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

    if (res != AEC_OK) {
        return AEC_PARA_ERROR;
    } else {
        return at_reassociate();
    }
}

static AT_ERROR_CODE scan_handler(at_para_t *at_para)
{
    at_scan_para_t cmd_para = {    /* default value */
        "",
        ""
    };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &cmd_para.mode,   AET_PARA | SIZE_LIMIT(sizeof(cmd_para.mode))},
        {APT_TEXT, &cmd_para.repeat, AET_LINE | SIZE_LIMIT(sizeof(cmd_para.repeat))},
    };
    s32 paracnt;
    int res;

    if (*at_para->ptr != AT_EQU) {

        res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        } else {
            return at_scan(NULL, NULL);
        }
    } else {
        at_para->ptr++;    /* skip '=' */

        res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 2) {
            return AEC_PARA_ERROR;
        }

        if (!(!strcmp(cmd_para.mode, "a") || !strcmp(cmd_para.mode, "p"))) {
            return AEC_PARA_ERROR;
        }

        if (!(!strcmp(cmd_para.repeat, "r"))) {
            return AEC_PARA_ERROR;
        }

        return at_scan(cmd_para.mode, cmd_para.repeat);
    }
}

static AT_ERROR_CODE CWLAPOPTscan_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    AT_DBG("Command not supported now!!\r\n");

    return AEC_OK;
}

static AT_ERROR_CODE CWLAPscan_handler(at_para_t *at_para)
{
    at_lapscan_para_t scanParam;
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, scanParam.ssid, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_lapscan_para_t))}
    };

    s32 paracnt;
    int res;
    u32 matchssid = 0;

    if (*at_para->ptr == AT_EQU) {
        at_para->ptr++;    /* skip '=' */
        res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);
        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }
        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        matchssid = 1;
    } else if ((*at_para->ptr != AT_CR) && (*at_para->ptr != AT_LF)) {
        return AEC_PARA_ERROR;
    }

    wlan_sta_scan_results_t results;
    results.ap = malloc(CMD_WLAN_MAX_BSS_CNT * sizeof(wlan_sta_ap_t));
    if (results.ap == NULL) {
        AT_ERR("no mem\n");
        return AEC_CMD_FAIL;
    }
    wlan_sta_scan_once();
    OS_MSleep(2000);
    results.size = CMD_WLAN_MAX_BSS_CNT;
    res = wlan_sta_scan_result(&results);

    if (res == 0) {
        int i = 0;
        if (matchssid == 1) {
            for (i = 0; i < results.num; ++i) {
                if (strcmp((const char *)scanParam.ssid, (const char *)results.ap[i].ssid.ssid) == 0) {
                    at_dump("+CWLAP:\"%s\",%d,\"%x:%x:%x:%x:%x:%x\",\"%d\"\r\n",
                            results.ap[i].ssid.ssid,
                            results.ap[i].rssi,
                            results.ap[i].bssid[0],
                            results.ap[i].bssid[1],
                            results.ap[i].bssid[2],
                            results.ap[i].bssid[3],
                            results.ap[i].bssid[4],
                            results.ap[i].bssid[5],
                            (int)results.ap[i].channel);
                    break;
                }
            }
        } else {
            for (i = 0; i < results.num; ++i) {
                at_dump("+CWLAP:\"%s\",%d,\"%x:%x:%x:%x:%x:%x\",\"%d\"\r\n",
                        results.ap[i].ssid.ssid,
                        results.ap[i].rssi,
                        results.ap[i].bssid[0],
                        results.ap[i].bssid[1],
                        results.ap[i].bssid[2],
                        results.ap[i].bssid[3],
                        results.ap[i].bssid[4],
                        results.ap[i].bssid[5],
                        (int)results.ap[i].channel);
            }
        }
        free(results.ap);
        return AEC_OK;
    }
    free(results.ap);
    return AEC_CMD_FAIL;
}

static AT_ERROR_CODE echo_on_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    at_cfg.localecho1 = 1;

    return AEC_OK;
}

static AT_ERROR_CODE echo_off_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    at_cfg.localecho1 = 0;

    return AEC_OK;
}

static AT_ERROR_CODE etf_enter_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    image_cfg_t img_cfg;

    img_cfg.seq = (image_get_running_seq() + 1) % IMAGE_SEQ_NUM;
    img_cfg.state = IMAGE_STATE_VERIFIED;
    image_set_cfg(&img_cfg);
    HAL_WDG_Reboot();

    return AEC_OK;
}

static AT_ERROR_CODE version_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    if (*at_para->ptr != AT_EQU) {
        return at_version(NULL);
    }
    return AEC_OK;
}

static AT_ERROR_CODE restore_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    if (*at_para->ptr != AT_EQU) {
        return at_restore(NULL);
    }
    return AEC_OK;
}

static AT_ERROR_CODE uart_config_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_uart_para_t uartParam;
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &uartParam.uart_id,    AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(uartParam.uart_id))},
        {APT_DI, &uartParam.baudrate,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(uartParam.baudrate))},
        {APT_DI, &uartParam.data_bits,  AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(uartParam.stop_bits))},
        {APT_DI, &uartParam.parity,     AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(uartParam.data_bits))},
        {APT_DI, &uartParam.stop_bits,  AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(uartParam.parity))},
        {APT_DI, &uartParam.hwfc,       AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(uartParam.hwfc))},
    };
    s32 paracnt;
    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        int res;

        at_para->ptr++;    /* skip '=' */
        res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);
        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }
        if (paracnt < 6) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>uart_id = %d\n", uartParam.uart_id);
        AT_WRN("------>baudrate = %d\n", uartParam.baudrate);
        AT_WRN("------>data_bits = %d\n", uartParam.data_bits);
        AT_WRN("------>parity = %d\n", uartParam.parity);
        AT_WRN("------>stop_bits = %d\n", uartParam.stop_bits);
        AT_WRN("------>hwfc = %d\n", uartParam.hwfc);
        return at_uart_config(uartParam.uart_id, uartParam.baudrate,
                      uartParam.data_bits, uartParam.parity,
                      uartParam.stop_bits, uartParam.hwfc);
    }
}

static AT_ERROR_CODE uart_config_save_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    return AEC_OK;
}

static AT_ERROR_CODE powersave_handler(at_para_t *at_para)
{
    at_powersave_para_t powersaveParam = { 0 };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &powersaveParam.powersaveMode, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(powersaveParam.powersaveMode))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {

        return AEC_PARA_ERROR;

    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>powersaveMode = %d\n",
               powersaveParam.powersaveMode);

        if (powersaveParam.powersaveMode == 1) {
            wlan_set_ps_mode(wlan_netif_get(WLAN_MODE_NONE), 1);
        } else if (powersaveParam.powersaveMode == 0) {
            wlan_set_ps_mode(wlan_netif_get(WLAN_MODE_NONE), 0);
        } else {
            return AEC_PARA_ERROR;
        }
        return AEC_OK;
    }
}

static AT_ERROR_CODE sleep_handler(at_para_t *at_para)
{
    at_sleep_para_t sleepParam = { 0 };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &sleepParam.sleepMode, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(sleepParam.sleepMode))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {

        return AEC_PARA_ERROR;

    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>sleepMode = %d\n", sleepParam.sleepMode);
        return at_sleep(sleepParam.sleepMode);
    }
}

static AT_ERROR_CODE wakeupgpio_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    at_gpiowakeup_para_t wakeupgpioParam = { 0 };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &wakeupgpioParam.gpioId, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(wakeupgpioParam.gpioId))},
        {APT_DI, &wakeupgpioParam.edge,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(wakeupgpioParam.edge))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 2) {
            return AEC_PARA_ERROR;
        }

        if (wakeupgpioParam.gpioId <= 0 && wakeupgpioParam.gpioId >= 9)
            return AEC_PARA_ERROR;
        if (wakeupgpioParam.gpioId == 4)
            return AEC_PARA_ERROR;
        if (wakeupgpioParam.edge != 0 && wakeupgpioParam.edge != 1) {
            return AEC_PARA_ERROR;
        }

        AT_DBG("------>wakeupgpio = %d EDGE = %d\n", wakeupgpioParam.gpioId, wakeupgpioParam.edge);
        return at_setwupio(wakeupgpioParam.gpioId, wakeupgpioParam.edge);
    }

    return AEC_OK;
}

static AT_ERROR_CODE wifi_mode_handler(at_para_t *at_para)
{
    at_wifiMode_para_t wifiParam = { 0 };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &wifiParam.wifi_mode, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(wifiParam.wifi_mode))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }

        AT_WRN("------>wifi mode = %d\n", wifiParam.wifi_mode);
        return at_wifi_mode(wifiParam.wifi_mode);
    }

}

static AT_ERROR_CODE set_ap_mode_handler(at_para_t *at_para)
{
    return at_wifi_mode(1);
}

static AT_ERROR_CODE dhcpd_start_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    struct dhcp_server_info dhcpd_info;

    at_dhcp_range_para_t dhcprangeParam;
    s32 paracnt;

    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &dhcprangeParam.en,         AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_dhcp_range_para_t))},
        {APT_DI, &dhcprangeParam.lease_time, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_dhcp_range_para_t))},
        {APT_IP, &dhcprangeParam.start_ip,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_dhcp_range_para_t))},
        {APT_IP, &dhcprangeParam.end_ip,     AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_dhcp_range_para_t))},
    };

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }

        printf("dhcprangeParam.en=%d dhcprangeParam.lease_time=%d lease_time.start=%s lease_time.end=%s\r\n", dhcprangeParam.en, dhcprangeParam.lease_time, dhcprangeParam.start_ip, dhcprangeParam.end_ip);

        //dhcpd_info.addr_start =  inet_addr(ipaddr_ntoa(&dhcprangeParam.start_ip));
        //dhcpd_info.addr_end = inet_addr(ipaddr_ntoa(&dhcprangeParam.end_ip));
        dhcpd_info.addr_start =  (dhcprangeParam.start_ip[3]<<24) | (dhcprangeParam.start_ip[2]<<16) | (dhcprangeParam.start_ip[1]<<8) | dhcprangeParam.start_ip[0];
        dhcpd_info.addr_end = (dhcprangeParam.end_ip[3]<<24) | (dhcprangeParam.end_ip[2]<<16) | (dhcprangeParam.end_ip[1]<<8) | dhcprangeParam.end_ip[0];
        dhcpd_info.lease_time = dhcprangeParam.lease_time;
        dhcp_server_start(&dhcpd_info);

    }

    return AEC_OK;

}

void set_reconnect_enable(void)
{
    reconnect_enable = 1;
}

void set_reconnect_disable(void)
{
    reconnect_enable = 0;
}

int get_reconnect_enable_status(void)
{
    return reconnect_enable;
}

AT_ERROR_CODE at_get_ssid_psk(char **ppara, char *ssid, char *pwd)
{
    char *para = *ppara;
    char *ptr = ssid;
    int i = 0;
    while (i < (SYSINFO_SSID_LEN_MAX + SYSINFO_PSK_LEN_MAX + 1)) {
        if (*para == '\\') {
            para++;
            i++;
            *ptr = *para;
            para++;
            ptr++;
        } else if (*para == '"') {
            para++;
            i++;
        } else if (*para == ',') {
            ptr = pwd;
            para++;
            i++;
        } else if (*para == AT_CR) {
            para++;    /* skip <CR> */
            if (*para == AT_LF) {
                para++;    /* skip <LF> */
            }
            break;
        } else {
            *ptr = *para;
            para++;
            ptr++;
        }
        i++;
    }
    return i < (SYSINFO_SSID_LEN_MAX+SYSINFO_PSK_LEN_MAX+1)?AEC_OK:AEC_PARA_ERROR;
}

static AT_ERROR_CODE join_ap_handler(at_para_t *at_para)
{
    AT_WRN("------>\n");

    AT_ERROR_CODE ret;

    at_joinAp_para_t joinParam;
    memset(&joinParam.ssid[0], 0x00, sizeof(at_joinAp_para_t));

    if (*at_para->ptr == AT_QUE) {
        return at_get_apinfo();

    } else if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_ssid_psk(&at_para->ptr, (char *)&joinParam.ssid,
                    (char *)&joinParam.pwd);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        AT_WRN("------> ssid = %s, pwd = %s\n", joinParam.ssid,
               joinParam.pwd);
        set_reconnect_disable();
        ret = at_join_ap(joinParam.ssid, joinParam.pwd);
        if (ret == AEC_OK) {
            return AEC_OK;    /* succeed */
        } else {
            at_dump("\r\n+CWJAP_CUR:%d \r\n", at_get_errorcode());
            return AEC_CMD_FAIL;
        }
    }
}

static AT_ERROR_CODE scan_attr_handler(at_para_t *at_para)
{
    AT_WRN("------>\n");

    return AEC_OK;
}

static AT_ERROR_CODE disconnect_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    if (*at_para->ptr != AT_EQU) {
        at_disconnect(NULL);
        set_reconnect_disable();
        OS_MSleep(200);
        return AEC_OK;
    }

    return AEC_OK;

}

static AT_ERROR_CODE setautoconnect_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_autoconnect_para_t autoParam = { 0 };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI,    &autoParam.autoconnectSwitch,        AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(autoParam.autoconnectSwitch))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>autoParam = %d\n", autoParam.autoconnectSwitch);

        if (autoParam.autoconnectSwitch == 1) {
            set_reconnect_enable();
        } else if (autoParam.autoconnectSwitch == 0) {
            set_reconnect_disable();
        } else {
            return AEC_PARA_ERROR;
        }
        return AEC_OK;

    }

    return AEC_OK;

}

static AT_ERROR_CODE set_dhcp_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_dhcp_para_t dhcpParam;
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &dhcpParam.dhcpmode, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_dhcp_para_t))},
        {APT_DI, &dhcpParam.dhcpen,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_dhcp_para_t))},
    };

    s32 paracnt;

    struct sysinfo *sysinfo = sysinfo_get();

    if (*at_para->ptr == AT_QUE) {
        int tmp = 0;
        if (at_cfg.apDhcp == 1)
            tmp |= 0x01;
        if (at_cfg.staDhcp == 1)
            tmp |= 0x02;

        at_dump("\r\n+CWDHCP:%02x \r\n", tmp);
        return AEC_OK;
    } else if (*at_para->ptr == AT_EQU) {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        if (dhcpParam.dhcpmode == 0) {
            at_cfg.apDhcp = dhcpParam.dhcpen;
        } else if (dhcpParam.dhcpmode == 1) {
            if (dhcpParam.dhcpen < 2) {
                at_cfg.staDhcp = dhcpParam.dhcpen;
                sysinfo->sta_use_dhcp = dhcpParam.dhcpen;
            }
        } else if (dhcpParam.dhcpmode == 2) {
            if (dhcpParam.dhcpen < 2) {
                at_cfg.staDhcp = dhcpParam.dhcpen;
                at_cfg.apDhcp = dhcpParam.dhcpen;
                sysinfo->sta_use_dhcp = dhcpParam.dhcpen;
            }
        } else
            return AEC_PARA_ERROR;
    } else {
        return AEC_PARA_ERROR;
    }
    sysinfo_save();
    return AEC_OK;
}

static AT_ERROR_CODE set_mac_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_mac_para_t macParam;
    at_para_descriptor_t cmd_para_list[] = {
        {APT_HEX, &macParam.mac[0], AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(unsigned char))},
        {APT_HEX, &macParam.mac[1], AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(unsigned char))},
        {APT_HEX, &macParam.mac[2], AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(unsigned char))},
        {APT_HEX, &macParam.mac[3], AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(unsigned char))},
        {APT_HEX, &macParam.mac[4], AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(unsigned char))},
        {APT_HEX, &macParam.mac[5], AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(unsigned char))},
    };

    s32 paracnt;
    struct sysinfo *sysinfo = sysinfo_get();

    if (*at_para->ptr == AT_QUE) {
        at_dump("+CIPSTAMAC:\"%02x:%02x:%02x:%02x:%02x:%02x\"\r\n",
                sysinfo->mac_addr[0],
                sysinfo->mac_addr[1],
                sysinfo->mac_addr[2],
                sysinfo->mac_addr[3],
                sysinfo->mac_addr[4],
                sysinfo->mac_addr[5]);
        return AEC_OK;
    } else if (*at_para->ptr == AT_EQU) {

        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 5) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>macParam.mac = %02x:%02x:%02x:%02x:%02x:%02x\n",
               macParam.mac[0], macParam.mac[1], macParam.mac[2],
               macParam.mac[3], macParam.mac[4], macParam.mac[5]);

        memcpy(sysinfo->mac_addr, macParam.mac, 6);
        sysinfo_save();

        return AEC_OK;
    }

    return AEC_OK;
}

static AT_ERROR_CODE set_ip_handler(at_para_t *at_para)
{

    AT_WRN("------>%s\n", __func__);
    at_ip_para_t ipParam;
    at_para_descriptor_t cmd_para_list[] = {
        {APT_IP, &ipParam.ip,      AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(ipParam.ip))},
        {APT_IP, &ipParam.getway,  AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(ipParam.ip))},
        {APT_IP, &ipParam.netmask, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(ipParam.ip))},
    };

    s32 paracnt;
    int res;

    struct sysinfo *sysinfo = sysinfo_get();

    if (*at_para->ptr == AT_QUE) {
        res = is_netconnet_ap();
        if (res == 1) {
            struct netif *nif = wlan_netif_get(WLAN_MODE_NONE);
            at_dump("+CIPSTA:ip:\"%s\"\r\n", ipaddr_ntoa(&nif->ip_addr));
            at_dump("+CIPSTA:gateway:\"%s\"\r\n", ipaddr_ntoa(&nif->gw));
            at_dump("+CIPSTA:netmask:\"%s\"\r\n", ipaddr_ntoa(&nif->netmask));
        }

    } else if (*at_para->ptr == AT_EQU) {
        at_para->ptr++;    /* skip '=' */

        res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }

        memcpy((char *)(&sysinfo->netif_sta_param.ip_addr), ipParam.ip,
               sizeof(at_ip_t));
        memcpy((char *)(&sysinfo->netif_sta_param.gateway),
               ipParam.getway, sizeof(at_ip_t));
        memcpy((char *)(&sysinfo->netif_sta_param.net_mask),
               ipParam.netmask, sizeof(at_ip_t));

        sysinfo->sta_use_dhcp = 0;
        sysinfo_save();
        AT_WRN("------>ipParam = %d,%d,%d,%d\n", at_cfg.ip_ipaddr[0],
               at_cfg.ip_ipaddr[1], at_cfg.ip_ipaddr[2],
               at_cfg.ip_ipaddr[3]);
        return AEC_OK;
    }

    return AEC_OK;
}

static AT_ERROR_CODE set_hostname_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_hostname_para_t hostParam;
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &hostParam.hostname, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(hostParam.hostname))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>hostParam = %s\n", hostParam.hostname);
        return at_set_hostname(hostParam.hostname);
    }

    return AEC_OK;
}

static AT_ERROR_CODE start_smartconfig_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    AT_DBG("Command not supported now!!\r\n");

    return AEC_OK;
}

static AT_ERROR_CODE stop_smartconfig_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    AT_DBG("Command not supported now!!\r\n");

    return AEC_OK;
}

static AT_ERROR_CODE start_DISCOVER_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    AT_DBG("Command not supported now!!\r\n");

    return AEC_OK;
}

static AT_ERROR_CODE stop_DISCOVER_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    AT_DBG("Command not supported now!!\r\n");

    return AEC_OK;
}

typedef struct {
    at_text_t address[AT_PARA_MAX_SIZE];
} at_domain_para_t;

typedef struct {
    at_text_t buffer[AT_PARA_MAX_SIZE];
} at_tcp_data_para_t;

typedef struct {
    at_text_t ssid[AT_PARA_MAX_SIZE];
    at_text_t psk[AT_PARA_MAX_SIZE];
    at_di_t chl;
    at_di_t ecn;
    at_di_t max_conn;
    at_di_t hidden;
} at_set_apcfg_para_t;

typedef struct {
    at_di_t linkId;
} at_close_para_t;

typedef struct {
    at_di_t mux;
} at_mux_para_t;

typedef struct {
    at_di_t mode;
} at_trans_mode_para_t;

typedef struct {
    at_ip_t dns;
} at_dns_para_t;

extern AT_ERROR_CODE at_network_status(char *param, at_callback_rsp_t *rsp);
extern AT_ERROR_CODE at_domain_query(char *dnsAdress);
extern AT_ERROR_CODE at_create_network_connect(at_network_para_t netpara,
                 at_callback_rsp_t *rsp);
extern AT_ERROR_CODE at_send_tcp_buffer(char *tcpBuffer);
extern AT_ERROR_CODE at_set_ap(char *ssid, char *psk, char chl, int max_conn);
extern AT_ERROR_CODE at_close_network(int linkId);
extern AT_ERROR_CODE at_mux_network(int mux);
extern AT_ERROR_CODE at_set_trans_mode(int mode);
extern AT_ERROR_CODE at_set_dns(char *dnsAdress);
extern AT_ERROR_CODE at_send_data(int linkId, char *dataBuffer,
                 s32 dataBufferLen);
extern AT_ERROR_CODE at_io_cfg(int ID, int mode, int pull);
extern AT_ERROR_CODE at_get_io_cfg(void);
extern AT_ERROR_CODE at_set_iodir_cfg(int ID, int mode);
extern AT_ERROR_CODE at_write_io_data(int ID, int data);
extern AT_ERROR_CODE at_read_io_data(int GPIOnum);
extern AT_ERROR_CODE at_http_ota(char *url);

static AT_ERROR_CODE network_status_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    at_callback_rsp_t rsp;

    rsp.vptr = c_atCmdRspBuf;
    memset(c_atCmdRspBuf, 0, 1024);

    if (*at_para->ptr != AT_EQU) {
        int res = at_network_status(NULL, &rsp);
        if (rsp.status == 1) {
            at_dump("%s \n", rsp.vptr);
        }

        return res;
    }
    return AEC_OK;
}

static AT_ERROR_CODE dns_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_domain_para_t domainParam;
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &domainParam.address, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(domainParam.address))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {

        return AEC_PARA_ERROR;

    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>dns address = %s\n", domainParam.address);
        return at_domain_query(domainParam.address);
    }

    return AEC_OK;
}

extern void ap_server_task(int port);
extern void ap_task_delete(void);
extern void close_server(void);
int flag_server;
static AT_ERROR_CODE ap_server_handler(at_para_t *at_para)
{
    at_tcpserver_para_t cmd_para;
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &cmd_para.enable, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(cmd_para.port))},
        {APT_DI, &cmd_para.port,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(cmd_para.enable))},
    };
    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_NO_PARA;
    } else {
        static int is_open_server = 0;

        at_para->ptr++;    /* skip '=' */
        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        printf("cmd_para.port=%d,cmd_para.enable=%d\r\n", cmd_para.port,
               cmd_para.enable);
        if (paracnt < 2) {
            return AEC_PARA_ERROR;
        }

        if (cmd_para.port < 0 || cmd_para.port > 65535) {
            return AEC_OUT_OF_RANGE;
        }

        if (cmd_para.enable == 1) {
            flag_server = 1;
            if (is_open_server == 0) {
                ap_server_task(cmd_para.port);
                is_open_server = 1;
            } else {
                return AEC_SOCKET_EXISTING;
            }
        } else if (cmd_para.enable == 0) {
            flag_server = 0;
            if (is_open_server == 1) {
                is_open_server = 0;
                ap_task_delete();
                close_server();
            }
        } else {
            return AEC_PARA_ERROR;
        }

        return AEC_OK;
        //return at_set_tcpserver(cmd_para.port, cmd_para.enable);
    }
}

static AT_ERROR_CODE ap_server_timeout_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    AT_DBG("Command not supported now!!\r\n");

    return AEC_OK;
}

static AT_ERROR_CODE ap_server_conn_handler(at_para_t *at_para)
{
    at_tcpserver_para_t cmd_para;
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &cmd_para.max_conn, AET_PARA | SIZE_LIMIT(sizeof(cmd_para.max_conn))},
    };
    s32 paracnt;

    if (*at_para->ptr == AT_QUE) {
        if (at_cfg.CIPMUX > 1)
            at_cfg.CIPMUX = 0;
        at_dump("\r\n+ CIPMUX:%d\r\n", at_cfg.CIPMUX);
        return AEC_OK;
    } else if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }

        return at_tcp_max_conn(cmd_para.max_conn);
    }
}

static int server_mode = 0;
int get_server_mode(void)
{
    return server_mode;
}

static AT_ERROR_CODE ap_server_receive_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    return AEC_OK;
}

static AT_ERROR_CODE ap_server_receivedata_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    AT_DBG("Command not supported now!!\r\n");

    return AEC_OK;
}

static AT_ERROR_CODE ap_server_receivelen_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    AT_DBG("Command not supported now!!\r\n");

    return AEC_OK;
}

#define MAX_CONNECTSTA 10
static AT_ERROR_CODE disp_apinfo_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    wlan_ap_stas_t stas;
    struct netif *nif = wlan_netif_get(WLAN_MODE_NONE);

    if (nif && wlan_if_get_mode(nif) == WLAN_MODE_HOSTAP) {
        stas.sta = (wlan_ap_sta_t *)malloc(MAX_CONNECTSTA * sizeof(wlan_ap_sta_t));
        if (stas.sta == NULL) {
            at_dump("no mem\n");
            return AEC_PARA_ERROR;
        }
        stas.size = MAX_CONNECTSTA;

        wlan_ap_sta_info(&stas);

        at_dump("sta_num: %d\n", stas.num);

        for (int i = 0; i < stas.num; i++) {
            at_dump("[%02d]Mac addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
                 i + 1, stas.sta[i].addr[0], stas.sta[i].addr[1],
                 stas.sta[i].addr[2], stas.sta[i].addr[3],
                 stas.sta[i].addr[4], stas.sta[i].addr[5]);
        }
        free(stas.sta);
        return AEC_OK;
    } else {
        at_dump("must in ap mode");
        return AEC_PARA_ERROR;
    }
}

static AT_ERROR_CODE disp_ipinfo_handler(at_para_t *at_para)
{
    int res;
    struct sysinfo *sysinfo = sysinfo_get();

    res = is_netconnet_ap();

#if defined(CONFIG_LWIP_V1)
    at_dump("+CIFSR:APIP,\"%s\"\r\n",
        ipaddr_ntoa(&sysinfo->netif_ap_param.ip_addr));
#else
    at_dump("+CIFSR:APIP,\"%s\"\r\n",
        ip4addr_ntoa(&sysinfo->netif_ap_param.ip_addr));
#endif
    at_dump("+CIFSR:APMAC,\"%02x:%02x:%02x:%02x:%02x:%02x\"\r\n",
            sysinfo->mac_addr[0],
            sysinfo->mac_addr[1],
            sysinfo->mac_addr[2],
            sysinfo->mac_addr[3],
            sysinfo->mac_addr[4],
            sysinfo->mac_addr[5]);
    if (res == 1) {
        struct netif *nif = wlan_netif_get(WLAN_MODE_NONE);
        at_dump("+CIFSR:STAIP,\"%s\"\r\n", ipaddr_ntoa(&nif->ip_addr));
    }

    return AEC_OK;
}

static AT_ERROR_CODE create_tcp_udp_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_network_para_t networkPara = {
        .linkId = -1,
        .keepAlive = 0,
    };
    at_callback_rsp_t rsp;

    rsp.vptr = c_atCmdRspBuf;
    memset(c_atCmdRspBuf, 0, 1024);

    at_para_descriptor_t cmd_para_type[] = {
        {APT_DI,   &networkPara.linkId, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_network_para_t))},
        {APT_TEXT, networkPara.type,    AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_network_para_t))},
    };

    at_para_descriptor_t cmd_para_TCP_list[] = {
        {APT_TEXT, networkPara.hostname,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_network_para_t))},
        {APT_DI,   &networkPara.port,      AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_network_para_t))},
        {APT_DI,   &networkPara.keepAlive, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_network_para_t))},
    };

    at_para_descriptor_t cmd_para_UDP_list[] = {
        {APT_TEXT, networkPara.hostname,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_network_para_t))},
        {APT_DI,   &networkPara.port,      AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_network_para_t))},
        {APT_DI,   &networkPara.localport, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_network_para_t))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_type,
                      TABLE_SIZE(cmd_para_type), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }
        if (networkPara.linkId > 4)
            return AEC_PARA_ERROR;

        if (strcmp(networkPara.type, "TCP") == 0) {
            res = at_get_parameters(&at_para->ptr, cmd_para_TCP_list,
                          TABLE_SIZE(cmd_para_TCP_list), &paracnt);
            if (res != AEC_OK) {
                return AEC_PARA_ERROR;
            }
        } else if (strcmp(networkPara.type, "UDP") == 0) {
            res = at_get_parameters(&at_para->ptr, cmd_para_UDP_list,
                          TABLE_SIZE(cmd_para_UDP_list), &paracnt);
            if (res != AEC_OK) {
                return AEC_PARA_ERROR;
            }
        } else if (strcmp(networkPara.type, "SSL") == 0) {
            return AEC_PARA_ERROR;
        } else {
            return AEC_PARA_ERROR;
        }

        AT_WRN("------>networkPara.linkId = %d,\n",
               networkPara.linkId);
        AT_WRN("------>networkPara.type = %s,\n", networkPara.type);
        AT_WRN("------>networkPara.hostname = %s\n",
               networkPara.hostname);
        AT_WRN("------>networkPara.port = %d,\n", networkPara.port);
        AT_WRN("------>networkPara.localport = %d,\n",
               networkPara.localport);
        AT_WRN("------>networkPara.keepAlive = %d\n",
               networkPara.keepAlive);
        res = at_create_network_connect(networkPara, &rsp);

        if (rsp.status == 1) {
            at_dump("%s \n", rsp.vptr);
        }
        return res;
    }

    return AEC_OK;
}

static AT_ERROR_CODE set_apcfg_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_set_apcfg_para_t apcfgpara;
    struct sysinfo *sysinfo = sysinfo_get();
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &apcfgpara.ssid,     AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_set_apcfg_para_t))},
        {APT_TEXT, &apcfgpara.psk,      AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_set_apcfg_para_t))},
        {APT_DI,   &apcfgpara.chl,      AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_set_apcfg_para_t))},
        {APT_DI,   &apcfgpara.ecn,      AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_set_apcfg_para_t))},
        {APT_DI,   &apcfgpara.max_conn, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_set_apcfg_para_t))},
        {APT_DI,   &apcfgpara.hidden,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_set_apcfg_para_t))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else if (*at_para->ptr == AT_QUE) {
        struct netif *nif = wlan_netif_get(WLAN_MODE_NONE);
        at_dump("+CIPSTA:ip:\"%s\"\r\n", ipaddr_ntoa(&nif->ip_addr));
        at_dump("+CIPSTA:gateway:\"%s\"\r\n", ipaddr_ntoa(&nif->gw));
        at_dump("+CIPSTA:netmask:\"%s\"\r\n", ipaddr_ntoa(&nif->netmask));
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 4) {
            return AEC_PARA_ERROR;
        }
        //memcpy(at_cfg. );
        memcpy(&sysinfo->wlan_ap_param.ssid, &apcfgpara.ssid,
               strlen(apcfgpara.ssid));
        memcpy(&sysinfo->wlan_ap_param.psk, &apcfgpara.psk,
               strlen(apcfgpara.psk));
        sysinfo->wlan_ap_param.ssid_len = strlen(apcfgpara.ssid);
        sysinfo->wlan_ap_param.channel = apcfgpara.chl;
        sysinfo_save();
        return at_set_ap((char *)&apcfgpara.ssid,
                 (char *)&apcfgpara.psk, apcfgpara.chl,
                 apcfgpara.max_conn);
    }
    return AEC_OK;
}

static AT_ERROR_CODE send_tcp_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_tcp_data_para_t tcpBuffer;
    at_para_descriptor_t cmd_para_list[] = {
        {APT_TEXT, &tcpBuffer.buffer, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_tcp_data_para_t))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>tcpBuffer.buffer= %s \n", tcpBuffer.buffer);
        return at_send_tcp_buffer(tcpBuffer.buffer);
    }
    return AEC_OK;
}

static AT_ERROR_CODE sendbuf_status_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    AT_DBG("Command not supported now!!\r\n");

    return AEC_OK;
}

static AT_ERROR_CODE close_network_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_close_para_t closePara = { -1 };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &closePara.linkId, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_close_para_t))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>closePara.linkId = %d \n", closePara.linkId);
        return at_close_network(closePara.linkId);
    }

    return AEC_OK;
}

static AT_ERROR_CODE set_multi_connect_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_mux_para_t muxPara = { -1 };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &muxPara.mux, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_mux_para_t))},
    };

    s32 paracnt;

    if (*at_para->ptr == AT_QUE) {
        if (at_cfg.CIPMUX > 1)
            at_cfg.CIPMUX = 0;
        at_dump("\r\n+ CIPMUX:%d\r\n", at_cfg.CIPMUX);
        return AEC_OK;
    } else if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>muxPara.mux = %d \n", muxPara.mux);
        return at_mux_network(muxPara.mux);
    }
    return AEC_OK;
}

static AT_ERROR_CODE set_translink_save_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    AT_DBG("Command not supported now!!\r\n");

    return AEC_OK;
}

static AT_ERROR_CODE set_transport_mode_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_trans_mode_para_t transModePara = { -1 };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI, &transModePara.mode, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_trans_mode_para_t))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */
        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>transModePara.mode = %d \n", transModePara.mode);
        return at_set_trans_mode(transModePara.mode);
    }
    return AEC_OK;
}

static AT_ERROR_CODE set_dns_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_dns_para_t dnsPara;

    at_para_descriptor_t cmd_para_list[] = {
        {APT_IP, &dnsPara.dns, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_dns_para_t))},
    };

    s32 paracnt;

    if (*at_para->ptr == AT_QUE) {
        //memcpy(&getdnsip,dns_getserver(0),sizeof(ip_addr_t));
#if defined(CONFIG_LWIP_V1)
        ip_addr_t getdnsip;
        getdnsip = dns_getserver(0);
        at_dump("+CIPDNS:\"%s\"\r\n", ipaddr_ntoa(&getdnsip));
#else
        const ip_addr_t *getdnsip;
        getdnsip = dns_getserver(0);
        at_dump("+CIPDNS:\"%s\"\r\n", ipaddr_ntoa(getdnsip));
#endif
        return AEC_OK;

    } else if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>dnsPara.dns = %d.%d.%d.%d \n", dnsPara.dns[0],
               dnsPara.dns[1], dnsPara.dns[2], dnsPara.dns[3]);
        return at_set_dns((char *)dnsPara.dns);
    }
    return AEC_OK;
}

static AT_ERROR_CODE read_tcp_data_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    return AEC_OK;
}

extern int is_disp_ipd;
static AT_ERROR_CODE ipdinfo_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_ipdinfo_para_t IPDParam = { 0 };
    at_para_descriptor_t cmd_para_list[] = {
        {APT_DI,    &IPDParam.IPDinfoSwitch,        AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(IPDParam.IPDinfoSwitch))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list,
                      TABLE_SIZE(cmd_para_list), &paracnt);

        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        AT_WRN("------>IPDParam = %d \n", IPDParam.IPDinfoSwitch);

        if (IPDParam.IPDinfoSwitch == 1) {
            is_disp_ipd = 1;
        } else if (IPDParam.IPDinfoSwitch == 0) {
            is_disp_ipd = 0;
        } else {
            return AEC_PARA_ERROR;
        }
        return AEC_OK;

    }

    return AEC_OK;

}

static AT_ERROR_CODE set_recv_data_mode_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);

    return AEC_OK;
}

extern int get_is_tcp_socket_empty(void);
static AT_ERROR_CODE send_data_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_sendData_para_t sendDataPara = { -1 };

    at_para_descriptor_t cmd_para_list_single[] = {
        {APT_DI, &sendDataPara.linkId,    AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_sendData_para_t))},
        {APT_DI, &sendDataPara.bufferlen, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_sendData_para_t))},
    };

    s32 paracnt;

    AT_WRN("at_cfg.CIPMUX== %d\r\n", at_cfg.CIPMUX);
    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list_single,
                      TABLE_SIZE(cmd_para_list_single),
                      &paracnt);
        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }

        if (paracnt < 2) {
            return AEC_PARA_ERROR;
        }

        if (get_is_tcp_socket_empty() == 0) {
            at_dump("link is not valid\r\n");
            return AEC_UNDEFINED;
        }


        if (send_cache.status == 0) {
            if (sendDataPara.linkId >= 4 || sendDataPara.bufferlen > CMD_SEND_DATA_MAX_LEN) {
                memset(&send_cache, 0, sizeof(cmd_send_cache_t));
                return AEC_PARA_ERROR;
            } else {
                send_cache.linkID = sendDataPara.linkId;
                send_cache.length = sendDataPara.bufferlen;
                send_cache.status = 1;
                return AEC_SEND_READY;
            }
        } else {
            sendDataPara.linkId = send_cache.linkID;
            sendDataPara.bufferlen = send_cache.length;
            sendDataPara.buffer = (at_text_t *)&send_cache.buf;
        }
        AT_WRN("------>sendDataPara.buffer = %d ,%s \n",
               sendDataPara.bufferlen, sendDataPara.buffer);

        return at_send_data(sendDataPara.linkId, sendDataPara.buffer,
                    sendDataPara.bufferlen);
    }

    return AEC_OK;
}

static AT_ERROR_CODE set_gpio_cfg_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_setgpio_para_t gpioPara = { -1 };

    at_para_descriptor_t cmd_para_list_single[] = {
        {APT_DI, &gpioPara.ID,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_setgpio_para_t))},
        {APT_DI, &gpioPara.mode, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_setgpio_para_t))},
        {APT_DI, &gpioPara.pull, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_setgpio_para_t))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list_single,
                      TABLE_SIZE(cmd_para_list_single), &paracnt);
        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }
        if (paracnt < 3) {
            return AEC_PARA_ERROR;
        }
        if (gpioPara.ID > 3)
            return AEC_PARA_ERROR;

        if (gpioPara.mode >= 2)
            return AEC_PARA_ERROR;

        if (gpioPara.pull >= 4)
            return AEC_PARA_ERROR;

        return at_io_cfg(gpioPara.ID, gpioPara.mode, gpioPara.pull);
    }

    return AEC_OK;
}

static AT_ERROR_CODE get_gpio_cfg_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    AT_ERROR_CODE res;

    res = at_get_parameters(&at_para->ptr, NULL, 0, NULL);

    if (res != AEC_OK) {
        return AEC_PARA_ERROR;
    } else {
        return at_get_io_cfg();
    }

}

static AT_ERROR_CODE set_gpio_dir_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_setgpiodir_para_t gpioPara = { -1 };

    at_para_descriptor_t cmd_para_list_single[] = {
        {APT_DI, &gpioPara.ID,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_setgpiodir_para_t))},
        {APT_DI, &gpioPara.mode, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_setgpiodir_para_t))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list_single,
                      TABLE_SIZE(cmd_para_list_single), &paracnt);
        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }
        if (paracnt < 2) {
            return AEC_PARA_ERROR;
        }

        if (gpioPara.ID > 3)
            return AEC_PARA_ERROR;

        if (gpioPara.mode >= 2)
            return AEC_PARA_ERROR;

        return at_set_iodir_cfg(gpioPara.ID, gpioPara.mode);
    }

    return AEC_OK;
}

static AT_ERROR_CODE set_gpio_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_writegpio_para_t gpioPara = { -1 };

    at_para_descriptor_t cmd_para_list_single[] = {
        {APT_DI, &gpioPara.ID,   AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_writegpio_para_t))},
        {APT_DI, &gpioPara.data, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_writegpio_para_t))},
    };

    s32 paracnt;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list_single,
                      TABLE_SIZE(cmd_para_list_single), &paracnt);
        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }
        if (paracnt < 2) {
            return AEC_PARA_ERROR;
        }

        if (gpioPara.ID > 3)
            return AEC_PARA_ERROR;

        if (gpioPara.data >= 2)
            return AEC_PARA_ERROR;

        return at_write_io_data(gpioPara.ID, gpioPara.data);
    }

    return AEC_OK;
}

static AT_ERROR_CODE read_gpio_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_di_t GPIOnum = -1;

    at_para_descriptor_t cmd_para_list_single[] = {
        {APT_DI, &GPIOnum, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(at_di_t))},
    };

    s32 paracnt = 1;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list_single,
                      TABLE_SIZE(cmd_para_list_single), &paracnt);
        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }
        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }

        if (GPIOnum > 3)
            return AEC_PARA_ERROR;

        return at_read_io_data(GPIOnum);
    }

    return AEC_OK;
}

static AT_ERROR_CODE ota_handler(at_para_t *at_para)
{
    AT_WRN("------>%s\n", __func__);
    at_otaurl_para_t otaParam;

    at_para_descriptor_t cmd_para_list_single[] = {
        {APT_TEXT, &otaParam.otaurl, AET_PARA | AET_LINE | SIZE_LIMIT(sizeof(otaParam.otaurl))},
    };

    s32 paracnt = 1;

    if (*at_para->ptr != AT_EQU) {
        return AEC_PARA_ERROR;
    } else {
        at_para->ptr++;    /* skip '=' */

        int res = at_get_parameters(&at_para->ptr, cmd_para_list_single,
                      TABLE_SIZE(cmd_para_list_single), &paracnt);
        if (res != AEC_OK) {
            return AEC_PARA_ERROR;
        }
        if (paracnt < 1) {
            return AEC_PARA_ERROR;
        }
        at_dump("url=%s\n", otaParam.otaurl);
        return at_http_ota(otaParam.otaurl);
    }

    return AEC_OK;
}
